Previous Page TOC Next Page


3 — Unleashing the Visual Basic 4.0 Product

by Conrad Scott

Visual Basic offers an assortment of functions for the manipulation of files and directories. This chapter covers the methods that can be used to open and read from files, write to files, navigate open files, and obtain information about a file. First, the basics of file input and output (I/O) are covered, followed by more advanced topics including reading files in blocks, binary chops, and file formats.

Examples in this chapter use variables created in the following format:

Preceded with


Variable Type


Example


i

Integer

iCounter

s

String

sDirName

l

Long

lByteCount

t

Type Structure

tMyType

gc_

Global Constant

gc_MyConstant

This key should simplify the examples provided. Additionally, each example explicitly declares the variables that it uses.

File I/O Basics

This section covers the basics of file input and output (I/O) in Visual Basic. This section mirrors functionality that already exists in Visual Basic 3.0. The section is organized into keywords in two major sections: creating and deleting files and directories, and accessing files. Each statement or function discussed is fully explained and examples are provided where appropriate.

Creating and Deleting Files and Directories

Files and directories can be created and deleted using numerous Visual Basic operations. The following covers each of the functions and statements associated with directory creation and deletion. This is an area where error checking is of the utmost importance, especially when creating and deleting across network drives.

MkDir

The MkDir function is used to create a new directory on a drive. The directory to be created is passed as an argument. For example:

MkDir "C:\WINDOWS\MYDIR"

This command creates a new directory given that the path provided exists. When creating a directory, many different errors can be encountered. The following example creates a directory. The function returns a value for Err as a result. If Err does not equal 0, an error occurred.

Function CreateDirectory(sDirectory As String)

    'Set Up Error Handler

    On Error GoTo CreateDirectory_ErrorHandler

    'Create the directory

    MkDir sDirectory

CreateDirectory_ErrorHandler:

    Select Case Err

        Case 0

    MsgBox "Error " & Error$ Occurred

        Case 1

    MsgBox "Error " & Error$ Occurred

        Case 2

    MsgBox "Error " & Error$ Occurred

    End Select

    'Return the error as the result of the function

    CreateDirectory = Err

End Function

Usage:

Dim sDirectory As String

Dim iRet as Integer

sDirectory = "C:\PROJECTS\PROJECT1"

iRet = Createdirectory(sDirectory)

If iRet = 0 Then

    'OK to continue...
Name

This statement is used to move a file from one directory to another on the same drive. This statement takes the following parameters:

sFromLoc—This is the name of the source file including the fully qualified path, for example: C:\OLDDIR\FROMFILE.TXT.

sToLoc—This is the name of the target file including the fully qualified path, for example, C:\NEWDIR\TOFILE.TXT.

There are several items to note when using this statement.

  1. Both sFromLoc and sToLoc must be on the same drive.

  2. If sFromLoc exists and is different from sToLoc, the file in sFromLoc is moved to the directory in sToLoc. If the filename in sNewLoc is different from sFromLoc, the file is renamed before being placed in the new location.

  3. You cannot move a directory using this statement, only a file.

  4. You cannot move a file that is currently open.

Attempting any of these actions results in an error (Err <> 0).

The following function uses the Name statement to rename a file and checks for errors:

Function RenameFile(sFromLoc as String, sToLoc as String)

    Name sOldLoc To sNewLoc

   'Check for errors

    Select Case Err

        Case 0' All is well, the file was renamed.

        Case 5 'Illegal Function Call

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case 68 'Device Unavailable

           MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case 70 'Permission Denied

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case 71 'Disk Not Ready

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case 75 'Path/File Access Error

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case 76 'Path Not Found

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case Else

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

    End Select

    RenameFile = Err

End Function

Usage:

Dim sOldFile As String

Dim sNewFile As String

Dim iRet As Integer

sOldFile = "C:\PROJECTS\PROJECT1\REPORT.TXT"

sNewFile = "C:\PROJECTS\PROJECT1\REPORT.TXT"

iRet = RenameFile(sOldFile, sNewFile)

If iRet = 0 Then

    'OK to continue...
RmDir

This function removes a directory from the hard drive. The following example removes a directory from the hard disk and checks for errors. The function returns the error code:

Function RemoveDirectory(sDirName as String)

    Dim sDirName as String

    'Remove the directory

    RmDir sDirName

    'Check for errors

    Select Case Err

        Case 0' All is well, the directory was removed.

        Case 68 'Device Unavailable

           MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case 70 'Permission Denied

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case 71 'Disk Not Ready

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case 75 'Path/File Access Error

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case 76 'Path Not Found

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

        Case Else

    MsgBox "Error " & Str$(Err) & ":" & "  " & Error$ & " occurred."

    End Select

    RemoveDirectory = Err

End Function

Usage:

Dim sDirName as String

Dim iRet as Integer

sDirName = "C:\PROJECTS\PROJECT1"

iRet = RemoveDirectory(sDirName)

If iRet = 0 Then

    'OK to continue
CurDir

This function returns the current directory. Imagine that the currently selected path on drive C is C:\PROJECTS, and on drive D it is D:\GAMES. The following examples denote the uses of this function:

  1. sWhereAmI = CurDir—This returns C:\PROJECTS.

  2. sWhereAmI = CurDir ("C")—This returns C:\PROJECTS.

  3. sWhereAmI = CurDir ("D")—This returns D:\GAMES.

Kill

This statement deletes a file from the disk. Use RmDir to remove a directory. This statement accepts wildcards thus deleting multiple files at the same time. For example:

Kill "C:\PROJECTS\DEADFILE.TXT"

Deletes the file "C:\PROJECTS\DEADFILE.TXT"

Kill "C:\PROJECTS\*.TXT"

Deletes all files with the TXT extension.

Usage:

Dim sFile As String

sFile = "C:\PROJECTS\PROJECT1\REPORT.TXT"

Kill sFile

If Err = 0 Then

    'OK to continue...

Accessing Files

Files can be opened and closed programatically from Visual Basic. The following section outlines the functions used in opening and closing files as well as the modes in which they can be opened. Examples are provided for each of the statements and functions where appropriate.

Open

This statement opens a file. There are several modes for which a file can be opened (discussed below). The syntax for the Open statement is

Open sFileName for Mode [Access sAccess] [Lock sLockParam] as iFileNum [Len = _iRecordLength]

There are several components of this statement.

  1. sFileName—The name of the file that you would like to open. This includes the fully qualified path the file.

  2. Access—This decides how you will access the file (read, write, and so on).

  3. Lock—This decides what other processes can do to the file while you have it open

  4. iFileNum—This is a file handle assigned to the file. The file handle is created using the FreeFile function (covered later in this section).

  5. Len—This is the amount of data that is to be read each time the file is accessed.

If the file in the Open statement does not exist and you are using Append, Output, Random, or Binary mode, the file is created. For example, if you open the file C:\WINDOWS\ SYSTEM\MYFILE.TXT and it does not exist, an empty file with that name is created and opened.

The following sections fully explain each of the parameters of the Open statement

File Mode—Mandatory Component of the Open Statement

The file mode is the manner in which the file is opened. Depending upon the type of operation you are performing (reading a file, writing to a file, and so on), the file is opened in a different mode. Visual Basic supports the following file modes: Input, Output, Random, and Binary. The following lists these modes:

Input

Sequential data input. This statement is used when you want to read data from a file. It sequentially reads the number of characters in RecLen each time the file is accessed.

Output

Sequential data output. This statement is used when you want to write data to a file. It sequentially writes the number of characters in RecLen each time the file is accessed.

Random

Binary

Binary file access is used when you want to move to a certain position in the file to retrieve or write data. For example, if you know that the data you are seeking is at position 256, open the file in Random mode, seek position 256, and read the data into your buffer using the Get statement. Data is placed into a binary file using the Put statement.

Append

The Append statement is used when you want to write data to a file and add it to the data that already exists in that file. New data is added (appended) to the bottom of the existing data. Use the Print# or Write# statements to add data to files opened in append mode.

Access Statement—Optional Component of the Open Statement

The Access statement decides the permissions you have when opening a file. This is a commonly ignored but very important parameter. The possible parameters are Read, Write, and Read Write. Read Write is only valid for files opened for random or binary files opened in Append mode.

Imagine you are opening a file to read some data that your program requires. You do not need to write any data to the file. Imagine you use the following code to read from the file:

Dim iFileNum as Integer

Dim sFileName as String

Dim sTemp as String

iFileNum = FreeFile

sFileName = "C:\PROJECTS\MYFILE.TXT"

'Open the file

Open sFileName for Input as iFileNum

'Keep going until the end of file is encountered

Do Until EOF(iFileNum)

    Line Input #iFileNum, , sTemp

    'Do some checking

    ...

Loop

If your file is 100 lines long, and that after 56 lines, your program encounters a GPF (this could never happen). The first 56 lines of the file that you were reading have been erased. This is because you opened the file for read and write. When the data was read into the sTemp variable, it was removed from the file. Solve this dilemma by opening the file for read only.

Open sFileName for Input Access Read as iFileNum

In this case you have opened the file for read only. You are still able to read data from the file and place it into the sTemp variable, but you are not endangering the source file if an error occurs. Additionally, other processes can read from this file at the same time without encountering an error. When you open a file as writable, you are "hogging" the file and no other Windows processes or applications can open it (even for read only).

Even though the file is opened as read only, you are still able to manipulate the data that is read into the sTemp variable for use within your Visual Basic application. If you need to modify data that is read from a file and then place it back into the file, you should open the file as writable. For example, imagine that you do the following (this is for example only, please don't do it):

  1. Read lines of data from a file that is open for read only using the Line Input statement, placing the line containing the data you require data into sTemp.

  2. Close the file.

  3. Change the data that is in sTemp.

  4. Open the file as writable.

  5. Write the line of data back out to the file using the Print# statement.

This operation has just erased the entire contents of the file and replaced it with the one line you modified. In this case, it would have been much simpler to open the file as writable, read the entire file into an array using Line Input, modify the desired array element (line of the file) and then write the entire array back into the file (first close the file and then open it in the output mode).


NOTE

If you are reading and writing to INI files, the Visual Basic file I/O functions are not the best method to use. Windows offers a variety of functions designed specifically for this purpose. The most useful of these functions are GetProfileString() and GetPrivateProfileString(). See the Windows System Developers Kit (SDK) help file for information about these functions.

Lock Statement

The Lock statement is somewhat like the Access statement except that it sets permissions that other operations have for the opened file. The options are:

  1. Shared—Other processes can both read and write to the file.

  2. Lock Read—Other processes cannot read the open file.

  3. Lock Write—Other processes cannot write to the open file.

  4. Lock Read Write—Other processes cannot read or write to the file.

The difference between this statement and the Access statement is that you can restrict access to a file for other processes even if you open the file as read only.

This example opens a file for input and locks the file for both reading and writing (no other processes can read or write to the file until it is closed using the Close statement.)

Open sFileName for Input Lock Read Write as x
FreeFile

The FreeFile function allocates a new file handle. This file handle is then used to open a file. The usage is as follows:

Dim iFileNum As Integer

Dim sFileName As String

sFileName = "C:\PROJECTS\PROJECT1\REPORT.TXT"

iFileNum - FreeFile

Open sFileName for Input as iFileNum

You can use literal numbers for file handles (Open sFileName for Input as 1), but it is not recommended. This can cause conflicts if you are opening several files (if you open a file as 1, forget to close it, and try to open another file as 1, you will get an error).

Record Length

The record length variable is used when you want to specify the number of characters read in each time the file is accessed. If the file is opened in Random mode, this value is the record length. For sequential files, this is the number of characters placed in the buffer variable. For example:

File opened in Random mode with a Record Length of 40

Dim iFileNum As Integer

Dim sFileName As String

sFileName = "C:\PROJECTS\PROJECT1\REPORT.TXT"

iFileNum = FreeFile

Open sFileName for Random as iFileNum Len = 40

File opened in Input mode (Input and Output are sequential modes) with a record length of 256

Dim iFileNum As Integer

Dim sFileName As String

Dim sTemp as String * 256

sFileName = "C:\PROJECTS\PROJECT1\REPORT.TXT"

Open sFileName for Input as iFileNum Len = 256

Input #iFileNum, ,sTemp

In this case, sTemp is 256 characters long so that is the number of characters read into the variable each time the file is accessed.

Close

This statement closes a file. Close takes one parameter, the file handle. This is the handle that was created with the FreeFile function and was used to open the file. In the following example, iFileNum is the file handle.

Close iFileNum

Multiple parameters can be used with this statement.

Close iFileNum1, iFileNum2, iFileNum3

If no parameter is used, all open files are closed. The Close statement releases all buffer space utilized by Visual Basic when the file was open.

Reset

The Reset statement closes any open files and writes the contents to disk. This statement operates the same as the Close statement without parameters.

Reading from and Writing to Files

Most of the operations on files involve the reading and writing of data. Files can also be used as databases to store information. The following section describes the functions and statements used to read from and write data to files.

Get

The Get statement is used to read data from a file and place it into a variable. The Get statement has the following syntax:

Dim iFileNum as Integer

Dim sData as String

iFileNum = FreeFile

Get #iFileNum, [iRecordNumber], sData

Depending on the mode for which the file is open, either iRecordNumber or sData is used to store the data that is read from the file. If sData is a variable length string, a 2-byte descriptor with the string length is placed in sData followed by the data. See the Visual Basic help topic "Get Statement" for additional detail on this statement.

If you choose to omit iRecordNumber, the commas must still be used.

Dim iFileNum as Integer

Dim sData as String

Get #FileNum,  , sData
Put

The Put statement writes the data from a variable into a file. This statement has the same parameters as the Get statement.

Dim iFileNum as Integer

Dim sData as String

sData = "This is a chunk of data being written to a file"

Put #FileNum, [iRecordNumber], sData

In this case, the data in sData is written to the file opened using the Open statement. This file is specified after the # (in this case #iFileNum.) This writing begins in one of the following positions within the file:

  1. The first byte in the file if the file is empty and this is the first Put statement

  2. The byte following the last Put statement

  3. The file position set using the Seek function

If you choose to omit iRecordNumber, the comma must still be used.

Dim sData as String

Put #FileNumber,  , sData

See the Visual Basic help topic "Put Statement" for additional information.

Input Statement and Function

The Input statement and function are used to read data from a file and assign it to a variable. Use the Input function only for files opened in Input or Binary mode. The difference between the Input statement and the Input function is that the Input function returns all characters read from the file. This includes carriage returns, line feeds, quotes, and leading spaces. The following is the syntax for the Input function:

Dim iFileNum As Integer

Dim sFileName As String

Dim iLen As Integer

Dim sData As String

'Number of characters to read from file

Dim iChars As Integer

iFileNum = FreeFile

sFileName = "C:\PROJECTS\DATA.TXT"

Open sFileName for Input as iFileNum

iLen = LOF(iFileNum)

'If the file is less than 32000 characters, set iLen to the file size. Otherwise set it to 32000

If iLen < 32000 Then

    iChars = LOF(iFileNum)

Else

    iChars = 32000

End If

'Input Function - get 32000 bytes and read it into sData

sData = Input iChars, #iFileNum,

The Input function reads 32,000 characters into the variable sData. This data includes carriage returns, line feeds, and so on.

The implementation of the Input statement is the same with the exception of the following line:

Input #x, sData

This statement reads the contents of the file without leading spaces, carriage returns, line feeds, and so on.


TIP

If you want to read bytes (instead of characters) from a file, use the InputB function.

Line Input

This statement reads a line at a time from a file. The end of a line is determined when a carriage return—Chr(13) or carriage return and line feed combination—Char(13) & Chr(10) is reached. The carriage return and/or line feed is not a part of the string that is read into the sString variable.


CAUTION

Do not use Line Input to read a binary file. There is a chance that there is no carriage return line feed in the file and therefore the Visual Basic attempts to read the entire contents of the file into the buffer at once. This can easily cause problems for your program (out of string space error messages, and so on)

The syntax for this statement is as follows:

Dim sData as String

Line Input #iFileNum, sData

This statement places the next line of the file into the variable sData.

Print #

The Print # statement writes data out to a sequential file. An example of when this statement is used is for the creation of a report. The following is the syntax of the Print # statement:

Dim iFileNum as Integer

Dim sData as String

sData = 'This is a string of data being written to a file using the Print # statement"

iFileNum = FreeFile

Print #iFileNum, sData

The following statement prints a blank line to the file:

Print #iFileNum,

If you are formatting data for a report, you can insert tabs and spaces so that the data is output in the correct manner. Remember to trim any leading and trailing spaces for data so that the tabs align correctly. The following line prints two columns (Name and Address) on the same line. The columns are separated by a tab.

Print # iFileNum, "Name"; Tab ; "Address"

The following line separates the Name and Address columns with two tabs.

Print # iFileNum, "Name"; Tab(2) ; "Address"

This line is the same as above, except it places 10 spaces before the Name field.

Print # iFileNum, Spc(10) ,"Name"; Tab(2) ; "Address"

This line writes the words "Have," "a," "Nice," and "Day" to the file. each word is separated by a space.

Print # iFileNum, "Have" ; " " ; "a" ; " " ; "Nice" ; " " ; "Day"

This final example writes the words "I love file I/O" to the file preceding by five tabs.

Print # iFileNum, Tab(5) ; "I love file I/O"

Data that is output using this statement is formatted for display, so the following rules apply:

  1. Keywords such as Date, Time, and so on are formatted using the local settings in your Control Panel.

  2. If you are printing a variable that is a result of a Boolean operation, either True or False is printed to the file.

  3. If there is nothing in the list of items to print to the file (Print #iFileNum,), a blank line is printed.

  4. If there is one variable in the list of items to be printed and that variable is Empty (variant that has not yet been assigned a value), nothing is printed to the file.

  5. If there is one variable in the list of items to be printed and that variable is NULL, the word NULL is printed to the file.

  6. If you are planning to read the data back out of the file at a later time, use Write # of the Print # statement.

File Positions

When reading and writing data to files, it is important to be able to set where in a file that operation is to take place. The following section details the functions and statements available in Visual Basic 4.0 for setting and retrieving the current position in a file. Examples are provided where appropriate.

Beginning of File (BOF)

Because I present the EOF function, I should explain that there is no BOF function used in Visual Basic file I/O. This function is used solely with databases to locate the first database record within a file.

EOF

This is a function that returns a Boolean (true or false) value that determines whether the file pointer has reached the end of the file. The following example demonstrates the usage of this function:

Dim sFileName as string

Dim iFileNum as Integer

iFileNum = FreeFile

sFileName = "C:\PROJECTS\PROJECT1\REPORT.TXT"

'Open the file

Open sFileName For Input As #iFileNum.

'Perform the following statement until the end of file has been reached.

Do While Not EOF(iFileNum)

    'Read in a line from the file and place it in sData

    Line Input #iFileNum, sData

Loop

'Close the file

Close #iFileNum
LOF

This function returns the size of a file in bytes that has been opened using the Open statement.

The following example demonstrates the use of the LOF function:

Dim sFileName as string

Dim iFileNum as Integer

iFileNum = FreeFile

sFileName = "C:\PROJECTS\PROJECT1\REPORT.TXT"

'Open the file

Open sFileName For Input As #iFileNum

'Place the length of the file into the FileLength variable

FileLength = LOF(iFileNum)

'Close the file

Close #iFileNum

If you want to know the length of a series of files and do not need to perform I/O, use the FileLen function, covered later in the chapter.

Loc

This function returns the current position of the file pointer within a file opened with the Open statement. The usage of this function is:

Dim sFileName as string

Dim iFileNum as Integer

iFileNum = FreeFile

sFileName = "C:\PROJECTS\PROJECT1\REPORT.TXT"

Dim sData as String * 256

Dim iPos as Integer

'Print the current file position to the form (before we start reading the file.)

Form1.Print "The current file position is: " & iPos

Do While iPos < LOF(iFileNum)

    'Grab 256 characters from the file and place into sData

    sData = sData & Input(256, #1)

    'Get the new file position

    iPos = Loc(iFileNum)

    Form1.Print "The current file position is: " & iPos

Loop

Close #1    ' Close file.
Seek Statement and Function

The Seek statement and function set and get the current position within a file. The file must have been opened using the Open statement. The Seek statement sets the position for the next read from or write to the open file. The following example opens a file for random and reads all the records into a variable. A record is comprised of a Customer type structure.

First we define a type called customer. This is done in the general declarations section of a module.

Type Customer

    sName As String * 30

    sAddress As String * 60

    iPurchases as Integer

End Type

Next, we create the function that reads the file.

Sub ReadCustomerFile

    Dim sFileName as String

    Dim i as Integer

    Dim iFileNum as Integer

    Dim iMax as Integer

    Dim tRecord() As Customer

    iFileNum = FreeFile

    sFileName = "C:\PROJECTS\PROJECT1\REPORT.TXT"

    ' Open the file

    Open sFileName For Random As iFileNum Len = Len(tRecord)

    'The next statement gets the number of records in the file

    iMax = LOF(iFileNum) \ Len(tRecord)

    ' Read the records in the file and place them into the sData array

    For i = 1 To Max

        Seek #1, RecordNumber    ' Set position.

        ReDim Preserve tRecord(i)

        Get #1, , tRecord(i)

    Next i

    Close iFileNum

For modes other than Random, Seek sets the position in the file where the next operation takes place.

File Information

Visual Basic 4.0 offers a variety of statements and functions for getting information about files. This section covers those and provides examples where necessary.

Dir

This function returns the name of a file meeting the specifications of the parameter passed to it. This function requires a path and allows the use of wildcards. One of the most useful places for this function is when you would like to gather a list of all files in a directory.

The following example creates a list of all files in the C:PROJECTS\PROJECT1 directory. Each file in the directory is added to the sDirList array. This listing includes all files in the directory.

Dim sDirectory as String

Dim sFile as String

Dim i as Integer

Dim sDirList() as String

sDirectory = "C:\PROJECTS\PROJECT1"

sFile = Dir$(sDirectory & "\*.*")

        i = 0

        Do While Len(sFile)

            ReDim Preserve sDirList(i)

            sDirList(i) = sDirectory & "\" & sFile

            sFile = Dir$

            i = i + 1

        Loop

This function can be modified to include only the Microsoft Word document (.DOC) files by changing one line.

sDirectory = "C:\PROJECTS\PROJECT1\*.DOC"

Additionally, this function has an attributes argument that allows you to specify the following file types:

Constant


Value


Description


vbNormal

0

Normal files

vbHidden

2

Hidden Files

vbSystem

4

System Files

vbVolume

8

Volume Label (all other attributes are ignored if this is used)

vbDirectory

16

Directory

The example is modified to screen for Hidden Files only.

Dim sDirectory as String

Dim sFile as String

Dim i as Integer

Dim sDirList() as String

sDirectory = "C:\PROJECTS\PROJECT1"

sFile = Dir$(sDirectory & "\*.*", 2)  '<<  Hidden Files Only

        i = 0

        Do While Len(sFile)

            ReDim Preserve sDirList(i)

            sDirList(i) = sDirectory & "\" & sFile

            sFile = Dir$

            i = i + 1

        Loop

FileAttr (Get and Set)

The FileAttr function provides two types of information about a file opened from Visual Basic using the Open statement. This function returns either the mode for which the file was opened, or the operating system file handle (not the Visual Basic file number retrieved using FreeFile). The syntax is as follows:

Dim iFileNum as Integer

Dim sFileName as String

Dim iMode as Integer

Dim iHandle as Integer

iFileNum = FreeFile

sFileName = "C:\PROJECTS\PROJECT1\REPORT.TXT"

Open sFileName for Output as iFileNum

'Get the mode for which the file is opened

iMode = FileAttr(iFileNum, 1)

'Get the operating system file handle for the open file

iHandle = FileAttr(iFileNum, 2)

The modes are as follows:

Mode


Value


Input

1

Output

2

Random

4

Append

8

Binary

16

In using this function, it is a good idea to establish global constants for each of the values. The following example declares global constants for each of the modes and places the FileAttr call into a function:

Global Const gcI_Input = 1

Global Const gcI_Output = 2

Global Const gcI_Random = 4

Global Const gcI_Append = 8

Global Const gcI_Binary = 16

Function GetFileInfo(sFileName as String, iInfoType as Integer)

    Dim iMode as Integer

    Dim iHandle as Integer

    'Get the mode for which the file is opened

    iMode = FileAttr(iFileNum, 1)

    'Get the operating system file handle for the open file

    iHandle = FileAttr(iFileNum, 2)

End Function

'The function is called in this manner:

Dim sFileName as String

Dim iRet as Integer

Dim iHandle as Integer

sFileName = "C:\PROJECTS\PROJECT1\REPORT.TXT"

iRet = GetFileInfo(sFileName, 1)

'Check to make sure that the file is opened in the correct mode. Using global constants makes the code

'more readable

If iRet <> gcI_Input then

   MsgBox "File opened in incorrect mode"

End If

iHandle = GetFileInfo(sFileName, 2)

FileLen

This function returns the length of a file in bytes. This operation is performed on a closed file. If the file is open, use the LOF function. This function can be used to add up the size of all files you choose to include so that you are sure of the total size. Additionally, the FileLen function is a good compliment to any status that you are providing to a user. For example, if you have 50,000 bytes of files to process, you can determine how far along you are after each file is completed. This can be passed to the gauge control.

The syntax for the FileLen function is as follows:

Dim iRet as Integer

Dim sFileName as String

sFileName = "C:\PROJECTS\PROJECT1\REPORT.TXT"

iRet = FileLen(sFileName)

This function is also useful in combination with the Dir function to get the size of all files selected. You modify your Dir function example.

Type DirInfo

    sFileName as String

    iFileSize as Integer

End Type

Dim sDirectory as String

Dim sFile as String

Dim i as Integer

Dim sDirInfo() as String

sDirectory = "C:\PROJECTS\PROJECT1"

'Get all files in the directory and place them into sDirInfo().

sFile = Dir$(sDirectory & "\*.*" )

        i = 0

        Do While Len(sFile)

            ReDim Preserve sDirList(i)

            sDirInfo(i).sFileName = sDirectory & "\" & sFile

            sDirInfo(i).iFileSize = FileLen(sDirectory & "\" & sFile)

            sFile = Dir$

            i = i + 1

        Loop

File Date and Time

The FileDateTime function returns a file's last modification date and time. If this file has not been modified, the return is the file creation date and time. The following builds on our FileLen example and adds the file date and time to the array of directory information:

Type DirInfo

    sFileName as String

    iFileSize as Integer

    iFileDateTime As Variant

End Type

Dim sDirectory as String

Dim sFile as String

Dim i as Integer

Dim sDirInfo() as String

sDirectory = "C:\PROJECTS\PROJECT1"

'Get all files in the directory and place them into sDirInfo().

sFile = Dir$(sDirectory & "\*.*" )

        i = 0

        Do While Len(sFile)

            ReDim Preserve sDirList(i)

            sDirInfo(i).sFileName = sDirectory & "\" & sFile

            sDirInfo(i).iFileSize = FileLen(sDirectory & "\" & sFile)

            sDirInfo(i).iFileDateTime = FileDateTime(sDirectory & "\" & sFile)

            sFile = Dir$

            i = i + 1

        Loop

File Manipulation

Now that we have covered the basics of file I/O using Visual Basic, it is time to move on to more advanced concepts. The next section of this chapter covers methods of searching a file for information, creates a database using a binary file, and delves into the realm of binary trees and binary chops.

Searching a File for Data

Now that you have opened and read data from a file, it's time to search for something. First take this from a text file point of view.

Imagine that you have a file listing the names and addresses of all employees in a company. A section of the file may look like this:

  1. Joe Smith 1234 Appletree Lane Atlanta, GA 30080

  2. Steve Jones 65 First Street Duluth, MN

  3. Mary Stevens 150 Cumberland Avenue San Diego, CA 92102

As you can see, this file is not formatted into defined fields. Imagine that you are searching this file for Steve Jones. The following example searches the file and returns the record number:

Function GetRecordNumber(sLastName as String)

    Dim iFileNum as Integer

    Dim sFileName as String

    Dim sTemp as String

    Dim I as Integer

    Dim iPos as Integer

    Dim iFound as Integer

    sFileName = "C:\PROJECTS\PROJECT1\EMPLOYEE.TXT"

    iFileNum = FreeFile

    Open sFileName for Input Access Read as iFileNum

    Do Until EOF(iFileNum)

        Line Input #iFileNum, sTemp

        'Record Number Counter

        I = I + 1

        iPos = Instr(sTemp, sLastName)

       'If we find the name, set iFound to true

        If iPos Then

            iFound = True

            Exit Do

        End If

Loop

    Close iFileNum

    If iFound = True Then

        GetRecordNumber = I

    Else

        GetRecordNumber = 0

    End If

End Function

This example returns the record number that holds the information. The example can be modified to return any element of information contained in the line.

First, review your data:

  1. Joe Smith 1234 Appletree Lane Atlanta, GA 30080

  2. Steve Jones 65 First Street Duluth, MN

  3. Mary Stevens 150 Cumberland Avenue San Diego, CA 92102

You are interested in getting the zip code from the customer record. Once you have the line of data from the file in sTemp, do the following:

Dim iZipCode as Integer

iZipCode = Right$(sTemp, 5)

This assumes that the zip code has five positions. If not, we have gotten incorrect data. A better implementation searches for the first space in the string starting from the right. Anything to the right of this space is the zip code. We trim the string first to ensure that there are no leading spaces.

Dim iZipCode as Integer

Dim iLen as Integer

Dim iFoundSpace

'Get rid of leading and trailing spaces.

sTemp = Trim$(sTemp)

'Get the length of the line and place it in iLen

iLen = Len(sTemp)

'Start at the rightmost character in the string searching for a space. step backwards 1 space at a time.

For I = iLen to 1 Step -1

    iPos = Instr(Mid$(sTemp,I, 1), " ")

    If iPos Then

        iFoundSpace = True

    End If

    Exit For

Next I

'Once we find the space, set the characters to the right of the space to the zip code (iPos - 1 ensures that we do not include the space in iZipCode)

If iFoundSpace = True Then

    iZipCode = Right$(sTemp, iPos - 1)

End If

Now, let's put it all together. The complete function searches for a last name in the list. It returns the record number, a space, and then the zip code.

Function GetRecordNumber(sLastName as String)

    Dim iFileNum as Integer

    Dim sFileName as String

    Dim sTemp as String

    Dim I as Integer

    Dim iPos as Integer

    Dim iFound as Integer

    Dim sZipCode as Integer

    Dim iLen as Integer

    Dim iFoundSpace

    sFileName = "C:\PROJECTS\PROJECT1\EMPLOYEE.TXT"

    iFileNum = FreeFile

    Open sFileName for Input Access Read as iFileNum

    Do Until EOF(iFileNum)

        Line Input #iFileNum, sTemp

        'Record Number Counter

        I = I + 1

        iPos = Instr(sTemp, sLastName)

       'If we find the name, set iFound to true and get the zip code.

        If iPos Then

             iFound = True

             'Get rid of leading and trailing spaces.

             sTemp = Trim$(sTemp)

             'Get the length of the line and place it in iLen

             iLen = Len(sTemp)

            'Start at the rightmost character in the string searching for a space _and step backwards.

    For I = iLen to 1 Step -1

            iPos = Instr(Mid$(sTemp,I, 1), " ")

            If iPos Then

                    iFoundSpace = True

            End If

            Exit For

    Next I

    'Once we find the space, set the characters to the right of the space to the _zip code (iPos - 1ensures that we do not include the space in iZipCode)

    If iFoundSpace = True Then

            sZipCode = Right$(sTemp, iPos - 1)

    End If

            Exit Do

        End If

    Loop

    Close iFileNum

    'If we found the record, return the record number and the zip code

    If iFound = True Then

        GetRecordNumber = I & " " & sZipCode

    Else

        'We did not find the record

        GetRecordNumber = 0

    End If

End Function

Binary File Formatting

The section above demonstrated the reading and searching of a text file for information. Although this is possible, it is not the most efficient method of data storage. As you saw from the example above, it is clearly not the easiest to manipulate. This section discusses the structured use of binary files for the storage and retrieval of data.

The section above demonstrated a rather crude database. In this section, you use the same data but store this data in a more organized format. Start with the data

  1. Joe Smith 1234 Appletree Lane Atlanta, GA 30080

  2. Steve Jones 65 First Street Duluth, MN

  3. Mary Stevens 150 Cumberland Avenue San Diego, CA 92102

First, create a structure in which to store the data.

Type CustomerRecord

    sFirstName as String * 20

    sLastName as String * 20

    sAddress as String * 50

    sCity as String * 10

    sState as String * 2

    sZipCode as String * 5

End Type

As you can see, this is a much more organized method of data storage. Next implement the program that adds records to the database. This assumes that the data has already been placed into the tCustomer structure by the user (added entering information into text boxes and clicking a command button, and so on).

Function AddRecord(sFileName as string, tCustomer as CustomerRecord)

    Dim iFileNum as Integer

    Open sFileName for Append as iFileNum

    Print # iFileNum, tCustomerRecord

    Close iFileNum

End Function

Remember that rather large function in the last section used to search the file for information? Implement the function again using the new file format. In this case, the following parameters are passed to the function:

  1. sFileName — The database file to open.

  2. sLastName — The last name to search for.

  3. tCustomer — A tCustomerRecord structure to place the record in once it is found.

Function GetRecord(sFileName as String, sLastName as String, tCustomer as tCustomerRecord)

    Dim iFileNum as Integer

    Dim tTemp as tCustomerRecord

    Open sFileName for Input Access Read as iFileNum Len=Len(sCustomer)

    Do Until EOF(iFileNum)

        Get # iFileNum, tTemp

        'Do the compare. Uppercase and trim each string so that the comparison _works.

        If UCase(Trim$(tCustomerRecord.sLastName)) = UCase(Trim$(sLastName)) Then

            iFound = True

    Exit For

        Endif

    Loop

    Close iFileNum

    'If we found it, place the data from the  database into the tCustomer structure _which was passed into the function.

    If iFound = True Then

        tCustomer = tTemp

    Endif

End Function

As you can see, this is a much simpler function, and because you are not manipulating strings with Instr and Trim$, there is less chance for error. Each piece of information is stored in an element of the structure.

Binary Chops

Now that you have implemented a more structured method of storing and retrieving data, take it one step further. Let's review:

You now have a database that is comprised of type structures containing customer information. The structure is as follows:

Type CustomerRecord

    sFirstName as String * 20

    sLastName as String * 20

    sAddress as String * 50

    sCity as String * 10

    sState as String * 2

    sZipCode as String * 5

End Type

You write all the records to a file using this structure as a format. When you want to search for a record (using the last name for example), you read the records from the file into a temp structure and check to see if Temp.LastName is equal to what you are searching for. This is fine if the database contains a limited number of records, but what if the database contains 100,000 records? This process is slow because each record must be compared to the desired last name to see if there is a match. Additionally, if Trim$ or Instr are used, the process is slowed further.

An alternative to this is to sort the records in the file and then search them. However, this does not increase the speed of the search because every record must still be checked. The answer to this dilemma is what is termed a binary chop.

Imagine that you have an array of 100 elements. The binary search method is structured as follows:

Divide the array by 2 to find the center and assign that value to iIndex. In the example, 100 elements / 2 = 50. Therefore, iIndex = 50. This is the place to start searching for the record.

Create 2 variables:

    iLBound - This is equal to the lowest element in the array or LBound(Array)

    iUBound - This is equal to the highest element in the array or UBound(Array)

Start searching the array at position iIndex (50). If what you are looking for is less than the value of the center element (in this case number 50) then you know that element number 50, as well as all elements with an array index greater than 50 are not the one that you are looking for. An example: Imagine that you are searching for c and that the center element is n. Because c is less than n, you know that n, and any item with an array index greater than n is not what you are looking for. Therefore, all elements of the array 50 and above are eliminated. To eliminate the elements, change iUBound to iIndex - 1. The -1 eliminates element number 50 from the remaining eligible elements.


CAUTION

Note that you do not actually affect the array during a binary chop. No items are actually from the array by ReDimming the array to a smaller size.

If what you are searching for is greater than the center element (iIndex), then you know that the center element and all elements with an array index less than it are eliminated, so we change iLBound to iIndex + 1.

Imagine that you have the following array :

0

A

1

B

2

C

3

D

4

E

5

F

6

G

7

H

8

I

9

J

10

K

Now, imagine that you are searching for the element "C."

Do the following:

  1. Divide the array by 2 to find the center and assign that value to iIndex. In this case iIndex = 5.

  2. Start searching the array at element iIndex (5) for the letter C. At index 5 you find the letter F. Because C is less than F, you know that element numbers 5 and greater are not what you are looking for, so you set iUBound to 4 (iIndex - 1). Although you have not changed the contents of the array, you are now using the following indexes to search it:

    iLBound = 0
    iUBound = 4

As far as your program is concerned, this array now only has five elements to search:

0

A

1

B

2

C

3

D

4

E

5

F

  1. Now reset your iIndex variable and check the array again (Divide the remaining array by 2 to find the center and assign that value to iIndex.) In this case use the following calculation:

    iIndex = CInt((UBound(Array) + LBound(Array)) / 2)

This results in 2. Therefore, the center of your array is now element number 2. Start the process over again. Look at element 2 and find the letter C. Because what you are searching for is greater than B, you know that element 1 and any element less than 1 (just 0 in this case) are not what you are looking for. Then reset the iLBound variable to 2. As far as your program is concerned, the array now is:

2

C

3

D

4

E

5

F

When there is an even number of elements in the array it can't be exactly split in half so you must remember to use CInt when calculating the index. In this case, it makes no difference if iIndex ends up at element 3 or 4. In either case, one more iteration of the loop is necessary to find the entry that you are searching for.

So... you searched a 10-element array with only four iterations to find what you were looking for. You accomplished the same task but took only 60% of the time to do it. In many cases this percentage will be as low as 33%. This is a very effective method not only for file I/O, but for array searching in general.

The following program fully implements the binary chop function. When an item is found, its count is incremented. This function uses a typed array with a structure as follows:

    Type Customer

        LastName as String

        iHits as Integer

    End Type

    Dim sArray as String

    'LBound and UBound variables

    iLBound = LBound(sArray)

    iUbound = UBound(sArray)

        'Keep going until either iLBound and iUBound are equal, or until iLBound is                            _greater than iUBound (no more elements to search).

        Do Until iLBound >= iUbound

            'Reset the iIndex variable each time through the loop (since the array                              _is 'smaller')

            iIndex = CInt((iUbound + iLBound) / 2)

            'Compare what we are searching for to the array entry

            iVal = StrComp(sSearchFor, sArray(iIndex).LastName)

            Select Case iVal

                Case -1    'What we are searching for is less than the array                                              _element

                    iUbound = iIndex - 1

                Case 0    'We have found what we are searching for. Increment the                          _counter and exit the loop.

          sArray(iIndex).iHits = sArray(iIndex).Hits + 1

          Exit For

                Case 1    'What we are searching for is greater than the array                  _element.

                    iLBound = iIndex + 1

            End Select

        Loop

File Formats

Now that you know the basics of file I/O from Visual Basic, you should know that many of the files on your system can be opened and read if the proper file format is known. The format for a C language type structure is:

typdef struct tag

TYPENAME {

    int        formatID;

    char           lenData[50];

    char           offData[65];

    int        formSize;

}TYPENAME;

With a very small amount of work (mostly understanding variable type conversions), these formats can be applied to Visual Basic.

Type TYPENAME

    formatID as Integer

    lenData as String * 50

    offData as String * 65

    formSize as Integer

End Type

This book provides you with an understanding of the structure of many files formats, including

Icon files

.ICO

Font files

.FNT

Windows metafiles

.WMF

Clipboard view files

.CLP

Executable files

.EXE

All these files can be opened from Visual Basic with the routines outlined in this chapter.

Additionally, when creating files for your application that you will access at a later time, it is important to ensure that you are accessing the correct file. Microsoft recommends that the first four bytes of your file contain information identifying the file as yours (this can be anything that you deem desirable. When accessing the file, ensure that you have the correct file by checking the first four bytes. The following example opens a Visual Basic 3.0 module and checks to see whether the file is saved as text. If it is, it is okay to continue.

Dim sFileName as String

Dim iFileNum as Integer

Dim sByte As String * 1

iFileNum = FreeFile

sFileName = "MODULE1.BAS"

Open sFileName For Binary Access Read As IFileNum

Get #iFileNum, , sByte

If sByte = Chr$(&HFC&) Then

    iRet = MsgBox("This module is saved in binary format.", 36, "Visual Basic 4 Unleashed")

ElseIf sByte = Chr$(&HFF&) Then

    iRet = MsgBox("This module is saved in text format.", 36, "Visual Basic 4 Unleashed")

End If

Close iFileNum

Summary

This chapter covered the basics of file input and output (I/O) in Visual Basic. First, the basics of file input and output (I/O) were covered. This included the functions and statements that Visual Basic provides for the opening, closing, and manipulation of files. This was followed by more advanced topics including the reading of files in blocks, binary chops, and file formats.

Previous Page TOC Next Page